<!DOCTYPE html>
<html lang="zh-CN">
<head>
	<meta charset="utf-8">
	<meta http-equiv="X-UA-Compatible" content="IE=edge">
	<meta name="viewport" content="width=device-width, initial-scale=1">
	<!-- 上述3个meta标签*必须*放在最前面，任何其他内容都*必须*跟随其后！ -->
	<title>PHP编程实战</title>

	<!-- Bootstrap -->
	<link href="../css/bootstrap.min.css" rel="stylesheet">

	<style>
		article {
			background: -moz-linear-gradient(top, #fb0, #f50);
		}
	</style>
	<!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
	<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
	<!--[if lt IE 9]>
	<script src="http://cdn.bootcss.com/html5shiv/3.7.2/html5shiv.min.js"></script>
	<script src="http://cdn.bootcss.com/respond.js/1.4.2/respond.min.js"></script>
	<![endif]-->
</head>
<body>
<article>
	<header><h1>第1章 面向对象</h1></header>
	<p>类的目的是封装对象的定义和行为，对最终用户隐藏它的具体实现，让用户按文档说明或期望的方式来使用类对象。</p>

	<p>如果没有指定类成员的可见性，那它默认为public。</p>

	<p>
		继承关系中子类和父类的关系是IS-A，即说“是一个”的意思，比如：员工类为父类，而每个经理都是一个员工。创建子类对象时，父类的构造函数和析构函数不会自动调用，需要用parent::来显式调用。而且在构造时应先执行父类的构造函数，再执行子类的，析构时则应先析构子类再执行父类的析构函数。</p>

	<p>
		PHP文件不要以?>结尾，因为在组合页之前它们会被加载或包含在一个“头”文件里，并且?>和EOF之间的任何额外空白将被注入在页面开头的html输出流。而且当使用header()函数发送HTTP送到浏览器时?>也可能是导致“output
		already started”错误的主要原因。</p>

	<p>
		当一个值赋值给一个不存在的成员时，__set方法被调用，当尝试去访问一个不存在（即没有在类中定义的）的成员时__get方法被调用。__isset()方法检验一个不存在的属性是否设置或是否有值，通常被定义为一个数组元素。用empty()函数检查变量是否为空时也会调用__isset方法。当在一个不存在的属性上调用unset()函数时，__unset方法会被自动调用。</p>

	<p>当用到不存在的方法时会调用__call方法，这个使用较少。应当没什么用的吧。</p>

	<p>__toString方法在把对象转换为字符串时或直接用打印语句输出时被自动调用。</p>

	<p>__clone方法在一个对象被用clone关键字复制时自动调用。对象变量赋值是创建一个引用，不会创建新的对象，而clone会创建一个新对象，并且不会触发构造方法。</p>

	<p>实现了PHP内部接口Iterator的任何类都可以在for循环中使用。<a href="IIterator.php">这个是示例</a>。PHP的类名和函数名不区分大小写，但还是严格点吧。</p>

	<p>PHP有一套名为SPL的标准库提供了很多强大功能，可以从http://www.php.net/manual/en/book.spl.php查看一下。</p>

	<p>
		使用static定义的变量称为类变量，它的作用域是类而不是对象，而且不用初始化就可以直接引用，要引用它需要使用ClassName::来引用，在类的内部则可以使用self::来引用。除了静态变量外还有静态方法，静态方法不能使用$this关键字引用对象变量和方法。</p>
</article>
<article>
	<header><h1>第2章 异常和引用</h1></header>
	<p>
		异常是同步事件，“同步”指的是代码本身对内部事件做出反应。抛出一个异常是指通过控制程序设计的一部分程序来处理这些事件。PHP中异常可以是类Exception的对象，也可以是任何扩展了Exception类的对象。异常经常用来调试程序，因为错误事件发生时至少包含以下四个信息：错误消息，错误代码，文件名，行号。</p>

	<p>
		PHP的预定义全局数组$argv[]用来引用命令行参数，全局变量argc表示参数个数。try代码块不是作用域块，因此在它里面定义的变量，在外面依然可见。PHP还有个魔术方法set_exception_handler()，用它可以指定一个“全体捕获”异常处理函数，去捕获所有未捕获的异常，在处理别人写的类时这个会很有用，比如像下面这样：</p>
	<pre>
		function dflt_handle(Exception $exc) {
			print "Exception:\n";
			$code = $exc->getCode();
			if(!empty($code)) {
				echo "Error code: $code\n";
			}
			echo $exc->getMessage() . "\n";
			echo "File:" . $exc.getFile()."\n";
			echo "Line:" . $exc.getLine()."\n";
			exit(-1);
		}

		set_exception_handler('dflt_handle');		//指定所有未捕获异常的处理函数
		$file = new SplFileObject("non_existing_file.txt", "r");	//这是PHP的内部类，虽然没有定义抛出异常，这种类在发生异常时会自动抛出
	</pre>
	<p>除非使用嵌套的try块，否则，PHP不会处理异常嵌套，这一点与C或C++没有区别。</p>

	<p>
		PHP中的引用仅是指一个对象的另外一个称呼。比如将一个对象变量赋值给另一个变量时，或者将对象变量传递给一个函数时，这时得到的都是该对象的引用，这样，在函数中对参数对象变量的修改就会体现在参数对象中。普通变量要实现引用操作的话只需要在前面加&，比如：&$x。注意，引用的最终结果只是存一个变量的别名，可用此别名直接操纵变量。</p>
</article>
<article>
	<header><h1>第3章 PHP在移动设备上的应用</h1></header>
	<p>
		要定制内容，先要明白在什么设备上使用，可参用$_SERVER['HTTP_USER_AGENT']超全局服务器变理来获得用户代理标头，还可以使用get_browser()函数，它的第一个参数可以是null或设置为实际的客户端名称就可以得到正在使用的客户端参数，但是这个函数依赖于服务器系统上的browscap.ini文件。为了检测到更多移动设备可以使用正则表达式，从网站http://ddectmobilebrowser.com可以得到。其实没有这么复杂。可以不用对终端这样斤斤计较。</p>

	<p>PHP官方用于检测设备类型的API被称为The New WUREFL
		API，可以从http://wurfl.sourceforge.net/nphp/上下载。对设备性能的测试包可从www.tera-wurfl.com上下载。个人感觉这些不是很有用。直接用CSS3就OK了。</p>

	<p>
		二维码QR的应用，有三种库可用：http://www.tcpdf.org这个库可生成嵌入pdf文件的二维码（没什么用处，而且这个包也太大了，16MB）,http://phpqrcode.sourceforge.net（这个库可以将二维码图像保存成文件）,http://code.google.com/p/gchartphp。</p>

	<p>这一章讲了对终端的检测技术，没什么实用价值，而且并不能保证准确。对于二维码PHPQRCode这个包很好用。示例代码如下：</p>
	<pre>
		require_once('phpqrcode/qrlib.php');

		QRcode::png('Hello world qrcode', 'qrcode.png'); //生成图像文件
		QRcode::png('Hello world qrcode'); 	//定向到浏览器
	</pre>
</article>
<article>
	<header><h1>第4章 PHP在社交媒体上的应用</h1></header>
	<p>这一节讲的是社交网站开放认证的应用。案例是Facebook和Twitter，没法测试，TMD。略过</p>
</article>
<article>
	<header><h1>第5章 前沿</h1></header>
	<p>在不同的子程序中通信最常使用的方法之一就是通过全局变量进行通信，命名空间解决的就是这一问题。命名空间的示例如下：</p>

	<p>domestic.php :</p>
	<pre>
		&lt;?php
		class animal {
			function __construct() {
				$this->type='dog';
			}
			function get_type() {
				return($this->type);
			}
		}
		</pre>
	<p>wild.php :</p>
	<pre>
		&lt;?php
		namespace wild;		//注意，此处使用了命名空间，这样在和上面的文件被同时引入时就避免了冲突。
		class animal {
		function __construct() {
			$this->type='tiger';
		}
		function get_type() {
			return($this->type);
		}
	}
	</pre>
	<p>下面是执行脚本script5.1.php :</p>
	<pre>
		#!/usr/bin/env php
		&lt;?php
		require_once('domestic.php');
		require_once('wild.php');
		$a=new animal();
		printf("%s\n",$a->get_type());
		$b=new wild\animal();		//注意，这是使用命名空间作完整路径的实例，一旦使用了命名空间只能这样对类寻址
		printf("%s\n",$b->get_type());
		use wild\animal as beast;	//这是使用命名空间的示例相当于给它取个别名
		$c=new beast();
		printf("%s\n",$c->get_type());
	</pre>
	<p>一旦使用了命名空间，类只能够使用“命名空间\类”来进行寻址。用use...as...语句可以将命名空间导入局部命名空间，而且可以使用更方便的名称来作为它的名称。</p>
	<p>命名空间是作用域块，如果一个文件中有多个命名空间，应当用大括号分别括起来。比如：namespace animial\wild{} namespace animal\domestic{}</p>
	<p>使用了命名空间后，再使用__autoload()函数自动加载就有了小小麻烦，需要制定一个目录层次结构映射命名空间结构，并在__autoload函数中将命名空间结构用的\替换为/（用str_replace函数即可）。当然在初始货类时还是一定要用带命名空间的全名才行。</p>
	<p>PHP可以像JavaScript一样使用匿名函数。array_reduce是一个Google Map-Reduce算法的PHP实现，它有两个参数，第一个是数组元素，第二个是回调函数，可以这样使用：</p>
	<pre>
		$array = range(1,100);
		$sum = function($x,$y){
			return $x + $y;
		};
		$sigma = array_reduce($array, $sum);
	</pre>
	<p>匿名函数也可用于函数的返回值。</p>
	<p>Nowdoc类似于Heredoc，但它不对任何内容作解析，包括变量标点。因此，可用它来嵌入PHP（变量不会被解析）甚至SQL代码而不用转义字符，它与Heredoc的区别只是在声明开头标识用单引号括起来，而结尾与Heredoc一样。比如：</p>
	<pre>
		$file = &lt;&lt;&lt;'EOT'
		这里面可以写入任何代码，都不会被解析
		&lt;&lt;&lt;EOT;
	</pre>
	<p>PHP提供了goto语句，但它只能在局部跳转，不能跳入循环，也不能跳出当前子例程。这个没什么用，只是作为最后的手段。事件和类驱动的程序设计已然不需要goto来破坏程序结构了。</p>
	<p>PHP提供了标准库称为SPL，类似于C++的标准模板库STL。它包含了一些类，对于标准程序结构，比如栈、堆、双端链表和优先级队列极为有用。它的文档在http://us3.php.net/manual/en/book.spl.php。</p>
	<p>SplMaxHeap/SplMinHeap这两个类可以将插入的数据以降序/升序排列。也可以扩展这两个类，但要实现其compare()方法，用来自定义排序规则。</p>
	<p>SplFileInfo类可能返回给定文件的信息。FileSystemIterator类虽然不以Spl开头，它也是SPL的一部分，与Unix的find命令功能相同，它取回目录树并返回所有结果的遍历。</p>
	<p>SPL在PHP5.3中不是新的，它在5.2版中就已存在，从5.3版开始它成为PHP的正式组成部分，不用安装，也不能卸载。</p>
	<p>PHP的Phar扩展实现了类似Java的jar档案打包的功能，能够将多个文件压缩到一个phar包中。要使用此命令，需要将php.ini中的phar.readonly值设置为off(默认为on)。打包命令示例：</p>
	<pre>
		phar pack -f animals.phar -c gz wild.php domestic.php //这条命令将后面的两个文件以gzip格式打包在animals.phar中，它还支持zip,bz2(bzip2)两个参数。
	</pre>
	<p>还可以对phar文档进行签名，以防篡改：</p>
	<pre>phar sign -f animals.phar -h sha1</pre>
	<p>在使用时，需要这样将包文件中的文件嵌入：</p>
	<pre>
		include 'phar://animals.phar/wild.php';
		include 'phar://animals.phar/domestic.php';
	</pre>
	<p>这一部分书上介绍的命令行打包方式运行不了，网页面的应用方式可以<a href="http://www.ibm.com/developerworks/cn/opensource/os-php-5.3new4/index.html">查看IBM上的一篇老文章</a> 。另外，还可以用apc_complile_file将PHP文件编译为字节码，可以提速度。<a href="http://us3.php.net/manual/en/book.apc.php">APC介绍</a>。 </p>
</article>
<article>
	<header><h1>第6章 表单设计与管理</h1></header>
	<p>数据验证主要在两个地方完成，一是客户端用JavaScript一是服务器端用PHP验证。JavaScript的onblur事件在表单元素去去焦点时发生。</p>
	<p>对电子邮件地址元素值可以用PHP内置的filter_var()函数。比如：filter_var('email@example.com', FILTER_VALIDATE_EMAIL)，第二个参数是email的验证规则，如果地址正常就回将地址原样返回，否则将返回false，如果要验证URL则可用FILTER_VALIDATE_URL。</p>
	<p>要用表单上传文件应当设置form元素的enctype属性值为multipart/form-data，它定义表单数据的编码方式。enctype属性的默认值是application/x-www-form-urlencoded，它用于处理上传文件异常的输入类型和非ASCII码。PHP用$_FILES超全局变量数组保存上传文件的元数据，这是个二维数组，比如:$_POST['upload_file_name']['name']，第一维是表单元素的名称，第二维是下标表示上传的原始文件名，第二维可用的值还有size/tam_name(服务器上的上传文件临时别名)/type/error（与上传文件相关的错误代码）。is_upload_file(tmp_name)函数可用于确定文件是不是通过HTTP POST上传的，比如：is_uploaded_file($_FILES['localfile']['tmpname'])，在上传结束后将临时文件移到最终储位置之前一定要先验证一下。移动文件使用函数move_uploaded_file()。</p>
	<p>如果要执行服务器上的命令可以使用shell_exec()函数。用escapeshellcmd()函数可以忽略恶意字符，在执行shell_exec()前最好先用它过滤一下。</p>
	<p>中间讲的缩略图示例和正则表达式略过。</p>
	<p>要转换字符为某种编码可使用mb_convert_encode()函数。</p>
</article>
<article>
	<header><h1>第7章 数据库集成 I</h1></header>
	<p>NoSQL数据库没有事务管理层，也没有模式。在NoSQL数据库调用时使用的是集合，它不同于表，因为NoSQL可以容纳各种行或文档。行和文档之间的区别在于，行有固定的结构，由关系模式来定义，但是文档没有。在传统意义上NoSQL数据库不存储行，但存储文档，并使用JSON（它的目的在于简化XML）把文档描述为对象。NoSQL多使用JavaScript作为内部数据库语言，同时结合JSON来操作文档，下面就是个JSON文档的例子：</p>
	<pre>
		var = {
			"key1":"value1",
			"key2": {
				"key3":"value3"
			}
			"key4":["a1", "a2", "a3"...]
		}
	</pre>
	<p>创建NoSQL数据库有两个目的，原始性能和可扩展性；低管理开销。在Linux下安装MongoDB只需用管理员身份执行：pecl install mongo即可。每种MongoDB数据类型都定义为一个PHP类，<a href="http://us3.php.net/manual/en/book.mongo.php">关于MongoDB的资料可以查看PHP在线手册</a>。一个NoSQL集合就是一个命名的文档集，相当于关系数据库的一张表，但是文档集并不要求有相同的结构。</p>
	<p>这一节讲的MongoDB安装后好像只能在PHP环境下执行。回头看完MongoDB的书再来看完这一节。P111-123。CouchDB没怎么听说过，略过。</p>
	<p><a href="http://sqlite.org">SQLite</a>是一个基于SQL的关系型的开源数据库，它以单个文件的形式存储，适合嵌入式系统。</p>
	<p>字段的NULL值只能用IS NULL或IS NOT NULL来判断，无法用等号。SQLite3支持 以下数据类型：NULL/Integer/Real（实数）/Text/Blob（二进制大型对象）。注意，它不支持日期时间类型。</p>
	<p>当不需要数据库游标时，应当使用对象的finalize()方法来关闭，以免带来混乱！</p>
	<p>使用prepare方式插入数据时，需要先定义insert语句，它的字段用占位符定义。占位符由以冒号开头的字母或数字定义。SQLite轻量级不需要服务器，应当很有用处，这本书讲得比较概括，以后可以专门看下。</p>
</article>
<article>
	<header><h1>第8章 数据库集成 II</h1></header>
	<p>MySQLi对象也不会抛出异常，需要手动抛出。MySQL提供了LOAD DATA命令用于加载CSV文件。</p>
	<p>在PHP中可以使用call_user_func_array函数来调用用户函数，它有两个参数，第一个参数是函数名，第二个参数是数组形式的参数列表，比如F($a1,$a2)，可以这样调用call_user_func_array("F", array($a1,$a2))。而对于对象方法$obj->F($a1,$a2)，则应这样调用call_user_func_array(array($obj, "F"), array($a1, $a2))。这个方法在变量个数未知的时候非常有用，将变量数组直接引即可。<a href="mysqlcreate.php">示例</a> </p>
	<p>MySQLi虽然功能强大取代了MySQL但是它仍缺少一些重要的功能，比如命名绑定和处理。</p>
	<p>PDO是PHP数据对象的缩写，它尝试把所有数据库扩展统一到单一化的API。它有两个层次：首先是通用的PDO接口；其次是特定数据库的驱动程序，它与PDO接口协同，实现与数据库连接的实际接口。</p>
	<p>PDO的想法固然不错，但是它需要执行预处理语句，这样在查询大量数据时会造成大量延迟。作者建议将它看作是beta版软件。用到时再说吧</p>
	<p>ADO这一节也略过，用MySQLi吧。</p>
	<p>最后一节讨论了全文搜索插件Sphinx。使用的是PostgreSQL。现在暂时不会用到。</p>
</article>
<article>
	<header><h1>第9章 数据库集成 III</h1></header>
	<p>这一章讲得是Oracle，这种高大上的数据库用不起。略过。</p>
</article>
<article>
	<header><h1>第10章 函数库</h1></header>
	<p>SimplePie库实现RSS和Atom订阅，可以从http://simplepie.org下载。<a href="simplepie.php">示例</a>这个版本已经有了变化，随后根据下载的文档自行修改，暂且用不到。</p>
	<p>TCPDF是用PHP生成PDF文件的库，它不需要外部库，可以www.tcpdf.org上找到，全面支持PHP GD和ImageMagick的图形、条形码、渐变图、HTML、CSS、字体、布局管理、页眉和页脚。</p>
	<p>Google Map无法访问。</p>
	<p><a href="phpmailer.php">接下来发一个电子邮件试下。</a>测试并没有成功。以后再研究。</p>
</article>
<article>
	<header><h1>第11章 安全</h1></header>
	<p>从来都不要信任数据及用户的企图。对来自用户的任何数据都要进行过滤及转换。甚至假定php.ini的配置也不安全。</p>
	<p>要初始化所有变量，比如有一个用户权限判定的变量$is_admin，一定要将它初始值设为false，这样可以确保它不会被register_globals覆盖。如果要引入文件，可以采用白名单的方式（不过感觉没什么用）。</p>
	<p>使用telnet可以伪装GET或POST访问，因此，对于所谓比较安全的POST提交表单的方式也一样不安全。下面是用telnet发送POST请求的示例：</p>
	<pre>
		telnet stackoverflow.com 80
		POST /search HTTP/1.1
		Host: stackoverflow.com
		Content-Type: applicationi/x-www-form-urlencoded
		Content-Length: 9
		q=php+xss
	</pre>
	<p>以上telnet指令相当于在stackoverflow.com网页上一个设置为method=post action = "/search"的表单执行提交动作。q是表单的参数。因此，当表单数据包含如银行的用户名和密码之类的敏感信息时，应使用安全套接层（SSL）对通信进行加密。</p>
	<p>一句话，不管是cookie还是session数据都是不安全的，甚至包括$_SERVER变量。</p>
	<p>协议/端口/子域（二级以上域名）是用来标识文档是否同源的依据。如果不对用户输入的数据进行转码处理，那么很容易就可以被手工输入的域值在网页中嵌入脚本，比如：以下代码：</p>
	<pre>
		&gt;&lt;script&gt;alert('XSS attack');&lt;/script&gt;&lt;
	</pre>
	<p>因此在接收用户变量之间，一定要用htmlspecialchars()函数对变量进行转换，消除威胁，最好是创建一个类用来有效地管理变量。</p>
	<p>会话ID可以在URL中取回或通过网络通信被攻击者窃听。为防御会话固定攻击，必须在每个脚本开始重新生成会话并且在php.ini中设置限制。在每个脚本开始处替换会话的指令如下：</p>
	<pre>
		session_start();
		session_regenerate_id();
	</pre>
	<p>在php.ini中设置使用cookie存储会话ID并阻止它出现在URL中：</p>
	<p>
		session.use_cookies = 1
		session.use_only_cookies = 1
		session.use_trans_sid = 0
	</p>
	<p>为了预防会话固定攻击，也可以保存某些$_SERVER变量的值，比如REMOTE_ADDR/HTTP_USER_AGENT和HTTP_REFERER等可以区分用户身份的变量，接着在每个脚本的开始处重新检查这些变量值的一致性，如果保存的值与实际值不同，可以用session_destroy()将其销毁。</p>
	<p>使用占位符来预防SQL注入，不要用直接执行带有用户提交变量的SQL语句。或者使用mysql_real_escape_string函数来处理变量后再执行。</p>
	<p>在Apache服务器上可以使用.htaccess限制访问及文件的可见性，也可为目录添加空的index文件以隐藏目录内容。对于敏感的数据应溇保存在网站目录之外。目录权限一般应设置为755而文件权限为644。</p>
	<p>在生产环境中，php.ini应当修改以下几个参数：</p>
	<pre>
		display_errors = Off
		display_start_errors = Off
		log_errors = On
		error_log = "/somehere/outside/of/web/root"	//日志文件一定不能在网站目录内
		track_errors = Off    //跟踪最后的铹误保存在全局变量$php_errormsg中。不要保存
		html_errors = Off 	//将链接插入到关于错误的文档
		expose_php = Off 	//不让服务器添加PHP在它的报头中，那样会导致PHP在服务器上被使用
		register_globals = Off 	//对5.3.0来说已过时
		magic_quotes_gpc = Off 	//对5.3.0来说已过时，用数据库转义替换

		//还有上面提到的三条关于session的设置

		//禁用高风险的函数
		disable_functions = crul_exec, curl_multi_exec, exec, highlight_file, parse_ini_file, passthru, phpinfo, proc_open, popen, shell_exec, show_source, system
		//对于类也有相同的指令可以禁用，disable_class =
		//可以强化PHP如何处理文件的访问和远程文件
		allow_url_fopen = Off //是否允许打开远端文件
		allow_url_include = Off //是否允许引用远端文件
		file-uploads = Off  //仅当脚本不需要上传文件时标用

		//下面两行限制PHP能打开文件所在的目录及子目录
		open_basedir = /the/base/directory/
		enable_dl = Off //设置了open_basedir才允许通过

		safe_mode = On
	</pre>
	<p>决不要将密码以明文的形式保存。使用散列算法sha1或sha2函数将密码加密，记得要用mt_rand()取得随机数与密码合在一起后再加密，但这样也要保存这个随机数。</p>
</article>
<article>
	<header><h1>第12章 使用Zend Studio for Eclipse、Bugzilla、 Mylyn和Subversion进行敏捷开发</h1></header>
	<p>这一节简单介绍了几个开发辅助工具，尤其侧重调试和团队中用这些工具软件的好处。讲得太过简单，并没有安装配置过程，看来要学好英文才行了。</p>
</article>
<article>
	<header><h1>第13章 重构、单元测试和持续集成</h1></header>
	<p>重构代码的目的如下：</p>
	<ul>
		<li>创建新的函数，减少重复代码</li>
		<li>用简化的语句或描述性的函数名替代复杂的逻辑表达式，提高代码的可读性</li>
		<li>从大型类中提取一些方法，并将它们移到新的或更为合适的类中</li>
		<li>减少控制结构（if/else,for, foreach,while,switch）的层次嵌套</li>
		<li>面向对象的设计变更</li>
	</ul>
	<p>《Pro PHP Refactoring》是一本讲重构的书出版于2010年可以看一下。将长的逻辑表达式转为独立的函数可以提高可读性，比如：</p>
	<pre>
		if(shouldWalk()) {
			goForWalk();
		}
		function shouldWalk() {
			return ((OWN_A_DOG && (!TIRED || HAVE_NOT_WALKED_FOR_DAYS)) || (NICE_OUTSIDE && !TIRED) || BORED);
		}
		function goForWalk() {
			echo "Going for a walk";
		}
	</pre>
	<p>上面这种代码重构使程序主体部分更易于阅读。需要将shouldWalk函数根据不同条件分类分为几个小函数，不要将一大堆条件堆在一起。</p>
	<p>重构的最理想状态一个类代表一个对象，一个函数只做一件事。如果一个函数做好几件事，那么应当将它拆分成几个更小的函数。降低耦合度，提高可读性，这样的目的也是为了便于单元测试。</p>
	<p>有两种广泛使用的PHP单元测试框架：<a href="http://www.phpunit.de/manual/current/en/index.html">PHPUnit</a>和<a href="https://github.com/sebastianbergmann/phpunit/www.simpletest.org/">Simpletest</a>。</p>
	<p>PHPUnit可用PEAR来安装，命令如下：</p>
	<pre>
		pear channel-discover pear.phpunit.de
		pear channel-discover components.ez.no
		pear channel-discover pear.symfony-project.com
		pear install --alldeps phpunit/PHPUnit
	</pre>
	<p>啊哈，PHPUnit官网有中文文档！太好了。PHPUnit所需的PHP扩展除<a href="http://xdebug.org">Xdebug</a>外都是PHP自带且打开的。Xdebug需要执行phpize来编译，<a href="https://github.com/xdebug/xdebug">下载Xdebug源代码</a>或压缩包解压后，进入源代码顶级目录（注意：路径中不能有空格！），执行phpize，命令，然后，执行./configure --enable-xdebug，结束后输入make进行编译，这将在源代码目录下生成modules/xdebug.so扩展程序。将它复制到/usr/lib64/php5/extensions目录下。在/etc/php5/conf.d目录下创建一个名为xdebug.ini的配置文件，内容如下：</p>
	<pre>
		; comment out next line to disable xdebug extension in php
		zend_extension=xdebug.so
	</pre>
	<p>重启apache服务器即可。</p>
	<p>对于PHPUnit的获取可以写一个脚本如下：</p>
	<pre>
		#!/usr/bin/env bash
		clean=0 # Delete phpunit.phar after the tests are complete?
		aftercmd="php phpunit.phar --bootstrap bootstrap.php src/tests"
		gpg --fingerprint D8406D0D82947747293778314AA394086372C20A
		if [ $? -ne 0 ]; then
			echo -e "\033[33mDownloading PGP Public Key...\033[0m"
			gpg --recv-keys D8406D0D82947747293778314AA394086372C20A
			# Sebastian Bergmann <sb@sebastian-bergmann.de>
			gpg --fingerprint D8406D0D82947747293778314AA394086372C20A
			if [ $? -ne 0 ]; then
				echo -e "\033[31mCould not download PGP public key for verification\033[0m"
				exit
			fi
		fi

		if [ "$clean" -eq 1 ]; then
			# Let's clean them up, if they exist
			if [ -f phpunit.phar ]; then
				rm -f phpunit.phar
			fi
			if [ -f phpunit.phar.asc ]; then
				rm -f phpunit.phar.asc
			fi
		fi

		# Let's grab the latest release and its signature
		if [ ! -f phpunit.phar ]; then
			wget https://phar.phpunit.de/phpunit.phar
		fi
		if [ ! -f phpunit.phar.asc ]; then
			wget https://phar.phpunit.de/phpunit.phar.asc
		fi

		# Verify before running
		gpg --verify phpunit.phar.asc phpunit.phar
		if [ $? -eq 0 ]; then
			echo
			echo -e "\033[33mBegin Unit Testing\033[0m"
			# Run the testing suite
			`$after_cmd`
			# Cleanup
			if [ "$clean" -eq 1 ]; then
				echo -e "\033[32mCleaning Up!\033[0m"
				rm -f phpunit.phar
				rm -f phpunit.phar.asc
			fi
		else
			echo
			chmod -x phpunit.phar
			mv phpunit.phar /tmp/bad-phpunit.phar
			mv phpunit.phar.asc /tmp/bad-phpunit.phar.asc
			echo -e "\033[31mSignature did not match! PHPUnit has been moved to /tmp/bad-phpunit.phar\033[0m"
			exit 1
		fi
	</pre>
	<p>测试驱动开发（TDD）的原则更进了一步，规定没有为代码编写单元测试，就不能编写新代码。它的基本原则如下：</p>
	<ol>
		<li>编写一个测试（由于没有待测试的代码，所以测试失败</li>
		<li>实现最少的功能使测试通过</li>
		<li>重复以上步骤</li>
	</ol>
	<p>以上就是单元测试，但是没有解释如何编写测试代码。以后慢慢研究吧。</p>
	<p>这几章不知所云，也是许译得太烂。持续集成（CI）我理解为版本控制比如Git。</p>
</article>
<article>
	<header><h1>第14章 XML</h1></header>
	<p>XML模式提供了对XML文档的附加约束，这样用来确保文档的有效性和数据的合理性。有三种最流行的模式生成语言：Document Type Definition(DTD)、XML schema和RELAXNG。</p>
	<p>用SimpleXML能很容易地把XML存储为PHP对象，反之亦然。<a href="animal.php">从字符串解析XML的示例。</a>在加载XML文档时，如果它是无效的PHP会给出辅助性警告信息，<a href="template.php">从文档加载XML的示例</a>。</p>
	<p>XML命名空间定义元素属于什么集合，防止数据模糊，否则不同的节点类型可能包售相同名称的元素。这样就不能直接通过元素名称访问元素内容，需要加入命名空间才能正常访问，而且在使用命名空间前需要先注册，如下:</p>
	<pre>
		$xml->registerXPathNamespace('dog', 'http://foobar.com/dog');
		$names = $xml->xpath("dog:name");
	</pre>
	<p>还可以用SimpleXML生成XML文档，需要先创建一个指向文档根元素的SimpleXMLElement对象，然后将里面的子元素添加上去。不幸的是，所有添加的元素都在一行显示，SimpleXML并不能产生元素换行。要想很好地输出XML需要使用DOMDocument类，但是它不能直接使用，仍要借肋SimpleXML生成XML原始代码。</p>
	<p>下一小节是关于用SimpleXML来使用RSS的实例，感觉RSS用户极少，略过。</p>
	<p>DOMDocument是比SimpleXML更为强大的XML操作扩展，它具有单独的属性对应XML的元素和属性值。它可以搜索元素的属性和值。对于很大的XML文档，一般使用XMLReader和XMLWriter进行操作。</p>
	<p>XML暂时到这儿吧，书上讲得并不详细，估计也很少用到，用到时再查。</p>
</article>
<article>
	<header><h1>第15章 JSON和Ajax</h1></header>
	<p>浏览器的请求在单独线程中进行，不会阻止脚本的执行，这称作异步。信息以JSON、XML格式或单纯文本形式被来回传递，浏览器与服务器之间的异步通信不需要重载整个页面，这被称为Ajax（即异步的JavaScript和XML）。</p>
	<p>Ajax不是一项单独的技术，它包括几个相互联系的部分：</p>
	<ul>
		<li>表示层：HTML或XHTML、CSS和DOM</li>
		<li>数据交换：JSON、XML、HTML或纯文本</li>
		<li>异步通信：JavaScript的XMLHttpRequest对象</li>
	</ul>
	<p>JSON是表示数据的一种y试式它有七种数据类型：strings、objects、arrays、numbers、true、false和null。字符串必须用双引号，可以包含转义字符\n、\t和\"。JSON对象用大括号括起来，JSON中的数组用[]括起来，数组和JSON对象可相互嵌套。包含的键值对用逗号分开，键名和值之间用冒号分隔。如下：</p>
	<pre>
		{
			"name":"Brian",
			"age" : 29
			"company": {
				"name": "CompanyName",
				"employees": 35
			},
			"likeColor":["red","green"]
		}
	</pre>
	<p>PHP内置JSON编解码函数分别是json_encode和json_decode。json_decode的可选参数$assoc默认值为false，当值为true时JSON对象被解码为关联数组，如果JSON解码失败或超出循环限制则返回null。json_last_error函数返回表示出错代码的整型值。它的返回值如下：</p>
	<ul>
		<li>JSON_ERROR_NONE	没有错误发生</li>
		<li>JSON_ERROR_DEPTH 超过了最大堆栈深度</li>
		<li>JSON_ERROR_CTRL_CHAR 发生控制特性错误，可能是有误的编码</li>
		<li>JSON_ERROR_STATE_MISMATCH 无效的或者是有缺陷的json</li>
		<li>JSON_ERROR_SYNTAX 发生语法错误</li>
		<li>JSON_ERROR_UTF8 有缺陷的UTF-8编码特性，也可能是有误的编码</li>
	</ul>
	<p><a href="json.php">关于JSON的示例</a>。</p>
	<p>Ajax的最大特点就是允许部分加载，这样可以在不重新加载页面的情况下，操作和得到最新数据，并与服务器进行交互。Ajax也可以实现同步，但通常是异步后台调用。Ajax的缺点是：</p>
	<ul>
		<li>浏览器后退按钮和书签不记录AJax状态</li>
		<li>搜索引擎不会对动态生成的内容进行索引</li>
		<li>对于非JavaScript用户来说功能降低是必要的，并且需要额外的工作</li>
		<li>关于屏幕阅读器的无障碍问题</li>
	</ul>
	<p>Ajax引擎作为一个媒介置于客户端和服务器之间，它操纵客户端的表示层，或者向服务器发送异步事件，如果是后者，服务器又会反过来更新客户端，因此无需刷新整个页面。</p>
	<p>同步是顺序执行的，一个请求完成后，才能继续下一个请求，而异步则不需等待一个请求完成，可以单独的并行的执行，即不必等一个页面提交请求后才得到服务器的响应，它们是被单独的并行地执行的。</p>
	<p>XMLHttpRequest对象通常简写为XHR，是由微软在2000年创建，它是一个API，通常由JavaScript实现。无需重载网页就能向服务器发送请求并接收响应，它的名称只是组成部分，并不能从字面上理解，比如：XML实际上可能是XML、JSON、HTML或纯文本文档，http也可以是https，而request也包括响应，并非只有请求。</p>
	<p>在JavaScript中这样来创建一个XMLHttpRequest对象：</p>
	<pre>
		var xhr = new XMLHttpRequest();
		xhr.open("GET", "animals.xml");
	</pre>
	<p>要设置请求的参数可以使用open()函数（见上行示例），它的可用参数如下：</p>
	<ul>
		<li>请求方式:  可用"GET", "POST", "HEAD", "PUT", "DELETE", "OPTIONS"</li>
		<li>URL: 请求响应的URL，可以是PHP, JavaScript, HTML,纯文本或其它文件类型</li>
		<li>是否异步（可选）：默认为true表示非拥塞调用</li>
		<li>用户名和密码：这两个参数都是可选的，在服务器需要验证时提供</li>
	</ul>
	<p>异步调用有个监听回调函数onreadystatechange，它不影响脚本的继续运行。同步调用没有监听，并且需要拥塞主脚本直到收到响应。如果要发送一个异步调用onreadystatechange回调函数将跟据请求对象的readyState属性作出响应。</p>
	<p>XMLHttpRequest默认发送的报头为application/xml;charset=_charset，其中_charset是实际用到的编码，比如utf-8。如果需要修改报头，使用setRequestHeader(header_name, header_value)函数即可。在发送请求前，需要调用状态处理的回调函数。<a href="listing.html">示例在此</a>，<a href="listingxml.html">这是一个抓取文件的示例</a>，如果要抓取HTML文件使用responseTEXT就可以。。 </p>
	<p>像jQuery这样的JavaScript API简化了XMLHttpRequest的使用，支持跨浏览器使用，使DOM操作变得更容易。<a href="jquery.html">示例</a>，<a href="jquery_draw.html">一个复杂点的示例</a>。 </p>
</article>
<article>
	<header><h1>第16章 结束语</h1></header>
	<p>这本书诚如作者所言，可以放在计算机旁随时备查，而不是和其他书放在一起。</p>
</article>

<!-- jQuery (necessary for Bootstrap's JavaScript plugins) -->
<script src="http://cdn.bootcss.com/jquery/1.11.2/jquery.min.js"></script>
<!-- Include all compiled plugins (below), or include individual files as needed -->
<script src="../js/bootstrap.min.js"></script>
</body>
</html>